package com.jse;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.SecureClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.jar.JarFile;

import javax.tools.ToolProvider;

/**
 * 提供了用于动态加载 JAR 文件以及编译 Java 文件的方法。
 */
public class Compiler {

    /** 保存已加载的类信息 */
    public static HashMap<String, Class<?>> CLASS = new HashMap<>();

    /**
     * 动态加载多个 JAR 文件到项目中。
     * 
     * @param jarFiles JAR 文件路径列表
     */
    public static void loadJars(List<String> jarFiles) {
        var urls = new ArrayList<URL>();
        jarFiles.forEach(jarFilePath -> {
            try {
                urls.add(URI.create("jar:file:/" + jarFilePath + "!/").toURL());
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        });

        var cl = new URLClassLoader(urls.toArray(new URL[0]), ClassLoader.getSystemClassLoader());

        jarFiles.forEach(jarFilePath -> {
            try {
                var entries = new JarFile(jarFilePath).entries();
                while (entries.hasMoreElements()) {
                    var entry = entries.nextElement();
                    if (entry.isDirectory() || !entry.getName().endsWith(".class")
                            || entry.getName().equals("module-info.class") || entry.getName().startsWith("META-INF/")) {
                        continue;
                    }
                    var className = entry.getName().substring(0, entry.getName().length() - 6);
                    className = className.replaceAll("/", ".");
                    var clazz = cl.loadClass(className);
                    CLASS.put(className, clazz);
                }
            } catch (IOException | NoClassDefFoundError | ClassNotFoundException e) {
                Log.get("compiler").debug("loadjars [" + jarFilePath + "] err:" + e.getMessage());
            }
        });
    }

    /**
     * 动态加载单个类文件。
     * 
     * @param path 类文件路径
     * @param name 类名
     * @return 加载的类
     */
    public static Class<?> loadClass(String path, String name) {
        var cl = new SecureClassLoader() {
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                try {
                    byte[] bytes = Files.readAllBytes(Path.of(path));
                    return super.defineClass(name, bytes, 0, bytes.length);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };

        try {
            return cl.loadClass(name);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 使用系统 Java 编译器编译 Java 文件。
     * 
     * @param filePath Java 文件路径
     * @return 编译成功返回 true，否则返回 false
     */
    public static boolean compilerJava(String filePath) {
        return ToolProvider.getSystemJavaCompiler().run(null, null, null,
                "-encoding", "UTF-8",
                "-classpath", filePath,
                filePath) == 0;
    }

    /**
     * 使用 javac 命令编译 Java 文件。
     * 
     * @param filePath Java 文件路径
     * @return 编译成功返回 true，否则返回 false
     */
    public static boolean compilerJavac(String filePath) {
        try {
            Process process = Runtime.getRuntime().exec(new String[]{"javac", filePath});
            int exitVal = process.waitFor();
            if (exitVal != 0) {
                var charset = File.separatorChar == '/' ? StandardCharsets.UTF_8 : Charset.forName("GBK"); // Windows 下使用 GBK
                System.out.println(new String(process.getErrorStream().readAllBytes(), charset));
                return false;
            }
            return true;
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 主方法用于测试编译和加载功能。
     * 
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        System.out.println(compilerJava("D:\\work\\jsx\\src\\test\\java\\com\\jse\\http\\Part.java"));
        Class<?> clazz = loadClass("D:\\work\\jsx\\src\\test\\java\\com\\jse\\http\\Part.class", "com.jse.http.Part");
        System.out.println(Refs.invokeMethod(clazz, "name1"));
    }
}